package com.greatchn.no_session_auth_code.config;

import com.greatchn.no_session_auth_code.bean.CustomClientDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

import javax.annotation.Resource;

/**
 * @CustomAuthorizationServerConfig: 自定义授权配置
 * @author: ZBoHang
 * @time: 2023/2/16 9:46
 */
@Configuration
@EnableAuthorizationServer
public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 客户端服务
     * 内存模式，会自动创建一个默认的客户端服务
     */
    @Resource
    private CustomClientDetailsService customClientDetailsService;
    @Resource
    private AuthenticationManager authenticationManager;

    /**
     * 配置令牌端点的安全约束
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 放开 /oauth/check 端点 供资源服务器校验token的合法性
        security.passwordEncoder(NoOpPasswordEncoder.getInstance())
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置客户端
     * .inMemory()
     *     .withClient()
     *     .secret("client_secret01")
     *     // 开启授权码模式 开启刷新令牌
     *     .authorizedGrantTypes("authorization_code", "implicit", "refresh_token")
     *     // 允许授权的范围 空表示全部
     *     .scopes("all_resource")
     *     // 配置资源id
     *     .resourceIds("resourceId01")
     *     // client_id的主体权限信息 客户端模式下生效
     *     // .authorities("***")
     *     // 是否自动授权 ture-自动 false-手动
     *     .autoApprove(Boolean.TRUE)
     *     // 返回带有授权码的重定向基础地址
     *     .redirectUris("https://www.baidu.com/")
     * authorization_code-授权码模式
     * implicit-简化模式
     * client_credentials-客户端模式
     * password-密码模式
     * refresh_token-刷新token
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.withClientDetails(this.customClientDetailsService).build();
    }

    /**
     * 配置令牌的访问端点和令牌管理服务
     * 默认的访问端点如下：
     * /oauth/authorize：授权端点，获取授权码
     * /oauth/token：令牌端点，获取访问令牌
     * /oauth/confirm_access：用户确认授权提交端点
     * /oauth/error：授权服务错误信息端点
     * /oauth/check_token：提供给资源服务访问的令牌验证端点
     * /oauth/token_key：提供公有密匙的端点，JWT模式使用
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 配置授权码的管理策略
        endpoints
                .authorizationCodeServices(authorizationCodeServices())
                // 配置token的管理
                .tokenServices(tokenServices())
                // 默认POST 指定GET方便测试
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
        // 密码模式** 需指定认证管理器
        endpoints.authenticationManager(this.authenticationManager);

    }

    /**
     * 配置授权码的存取方式
     * INMEMORY JDBC RANDOM
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {

        return new InMemoryAuthorizationCodeServices();
    }

    /**
     * 配置令牌管理服务
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {

        DefaultTokenServices services = new DefaultTokenServices();
        // 配置客户端详情服务，获取客户端的信息
        services.setClientDetailsService(this.customClientDetailsService);
        // 支持刷新令牌
        services.setSupportRefreshToken(true);
        // 配置令牌的存储方式，此时采用内存方式存储
        services.setTokenStore(tokenStore());
        // 访问令牌有效时间2小时
        services.setAccessTokenValiditySeconds(7200);
        // 刷新令牌的有效时间3天
        services.setRefreshTokenValiditySeconds(259200);

        return services;
    }

    /**
     * 配置令牌管理策略
     * JDBC REDIS INMEMORY JWT...
     */
    @Bean
    public TokenStore tokenStore() {

        return new InMemoryTokenStore();
    }
}
